#!/usr/bin/env node
/**
 * @license
 * Copyright 2018 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */

/**
 * Updates the @tensorflow/tfjs dependency to the specified version in all
 * example package.json files.
 *
 * The examples that depend on @tensorflow/tfjs-node are currently not
 * handled by this script.
 *
 * Usage examples:
 *
 * - To update only the examples that do not use tfjs-node or tfjs-node-gpu:
 *   ./update-tfjs-version 1.2.8
 * - To update only the examples that use tfjs-node or tfjs-node-gpu:
 *   ./update-tfjs-version --node 1.2.8
 */

const fs = require('fs');
const path = require('path');

const tfjsTag = '@tensorflow/tfjs';
const tfjsNodeTag = '@tensorflow/tfjs-node';
const tfjsNodeGpuTag = '@tensorflow/tfjs-node-gpu';

const argv = process.argv.slice();  // Avoid mutating `process.argv`.
if (!(argv.length === 3 || argv.length === 4)) {
  // Invalid syntax. Print usage help.
  console.log('Usage: update-tfjs-version [--node] <TARGET_TFJS_VER>`');
  console.log();
  console.log('Args:');
  console.log(
      '  --node: Update only examples that use tfjs-node/tfjs-node-gpu.');
  console.log(
      '          If this flag is not specified, only those examples');
  console.log(
      '          that don\'t use tfjs-node or tfjs-node-gpu will be');
  console.log(
      '          updated.');
  process.exit(1);
}

let isNodeExamples = false;
if (argv.indexOf('--node') !== -1) {
  argv.splice(argv.indexOf('--node'), 1);
  isNodeExamples = true;
  console.log('Will update only Node.js examples\n');
}

const targetTfjsVer = argv[2];
const targetVer = `^${targetTfjsVer}`;

const dirItems = fs.readdirSync(__dirname);
for (const item of dirItems) {
  if (fs.lstatSync(item).isDirectory()) {
    const dirPath = path.join(__dirname, item);
    const packageJsonPath = path.join(dirPath, 'package.json');
    if (fs.existsSync(packageJsonPath)) {
      const packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
      const deps = packageJson['dependencies'];
      if (deps == null) {
        continue;
      }

      const devDeps = packageJson['devDependencies'];
      if (isNodeExamples) {
        const depCPU = deps == null ? null : deps[tfjsNodeTag];
        const depGPU = deps == null ? null : deps[tfjsNodeGpuTag];
        const devDepCPU = devDeps == null ? null : devDeps[tfjsNodeTag];
        const devDepGPU = devDeps == null ? null : devDeps[tfjsNodeGpuTag];
        // Update only examples that use tfjs-node / tfjs-node-gpu.
        if ((devDepCPU != null || devDepGPU != null) ||
            (depCPU != null || depGPU != null)) {
          console.log(`${item}:`);
          if (devDepCPU != null) {
            devDeps[tfjsNodeTag] = targetVer;
            console.log(
                `  devDependencies["${tfjsNodeTag}"]: ` +
                `${devDepCPU} --> ${targetVer}`);
          }
          if (devDepGPU != null) {
            devDeps[tfjsNodeGpuTag] = targetVer;
            console.log(
                `  devDependencies["${tfjsNodeTag}"]: ` +
                `${devDepGPU} --> ${targetVer}`);
          }
          if (deps[tfjsTag] != null) {
            const oldVer = deps[tfjsTag];
            deps[tfjsTag] = targetVer;
            console.log(`  dependencies["${tfjsTag}"]: ${oldVer} --> ${targetVer}`);
          }
          if (depCPU != null) {
            deps[tfjsNodeTag] = targetVer;
            console.log(
                `  dependencies["${tfjsNodeTag}"]: ` +
                `${depCPU} --> ${targetVer}`);
          }
          if (depGPU != null) {
            deps[tfjsNodeGpuTag] = targetVer;
            console.log(
                `  dependencies["${tfjsNodeGpuTag}"]: ` +
                `${depGPU} --> ${targetVer}`);
          }
          fs.writeFileSync(
            packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
          console.log('\n');
        }
      } else {
        // Update only tfjs examples, i.e., the ones that don't use tfjs-node
        // or tfjs-node-gpu.
        if (deps[tfjsNodeTag] != null ||
            devDeps != null && devDeps[tfjsNodeTag] != null) {
          console.log(
              `*** Skipping example with dependency or devDependency ` +
              `on tfjs-node: ${item}`);
          continue;
        }

        if (deps[tfjsTag] != null) {
          if (deps[tfjsTag] === targetVer) {
            console.log(`${item}: Already at target version (${targetVer})`);
          } else {
            const oldVer = deps[tfjsTag];
            deps[tfjsTag] = targetVer;
            fs.writeFileSync(
                packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
            console.log(`${item}: dependencies["${tfjsTag}"]: ${oldVer} --> ${targetVer}`);
          }
        }
      }
    }
  }
}
